%{
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "opcode.h"
#include "directive.h"
#include "registers.h"
#include "symboltable.h"

	int get_id (void);
	void print_data_string (FILE* stream, char *d);
	void yyerror (char *);

	extern int yyline;
	extern int yylval;
	extern char *yytag;
	int length = 0;
	int datawords = 0;
	int instructionwords = 0;

	struct symbol *symbol_table = 0;

	char tempfilename [32];
	FILE *tempfile = 0;
	char buf [1024];
%}
%token OPERATOR
%token ID
%token REGISTER
%token NUMBER
%token LABEL
%token TEXT
%token DATA
%token WORD
%token STRING
%token GLOBAL
%token STRINGLITERAL
%token INVALID

%start program

%%
program: globals datasection textsection;

globals: global globals
       | ;
global: GLOBAL idlist;
idlist: globalid idlist
      | globalid;
globalid: ID {symbol_table = insert (symbol_table, yytag, -1, -1, S_GLOBAL);};

textsection: TEXT {fprintf (tempfile, "\n.text\n");} instructions
           | ;

instructions: instruction instructions 
            | ;
instruction: operator operands {$$ = ($1 << 26) | $2; fprintf (tempfile, "%x ", $$); instructionwords ++;}
           | textlabel;
operands: register register register {$$ = ($1 << 22) | ($2 << 18) | ($3 << 14);}
        | register number {$$ = ($1 << 22) | ($2 & 0x3FFFFF);} 
				| register textid {$$ = ($1 << 22);}
        | register register {$$ = ($1 << 22) | ($2 << 18);}
        | register register number {$$ = ($1 << 22) | ($2 << 18) | ($3 & 0x3FFFF);}
        | register register textid {$$ = ($1 << 22) | ($2 << 18);}
        | {$$ = 0;};

textlabel: LABEL {symbol_table = insert (symbol_table, yytag, instructionwords, S_TEXT, S_LOCAL);}; 
textid: ID 
		{
			fprintf (tempfile, "<%s>", yytag);
		};
operator: OPERATOR {$$ = yylval;};
register: REGISTER {if (yylval < 0) yyerror ("invalid register"); else $$ = yylval;};
number: NUMBER {$$ = yylval;};

datasection: DATA {fprintf (tempfile, ".data\n");} dataitems
           | ;
dataitems: dataitem dataitems
         | ;
dataitem: datalabel WORD numbers
        | datalabel STRING datastring;
numbers: datanumber numbers
       | datanumber;

datanumber: NUMBER {datawords += 1; fprintf (tempfile, "%x ", yylval);};
datastring: STRINGLITERAL {length = strlen (yytag) + 1; datawords += length; print_data_string (tempfile, yytag);};
datalabel: LABEL {symbol_table = insert (symbol_table, yytag, datawords, S_DATA, S_LOCAL);};

%%
void yyerror (char *message)
{
	fprintf (stderr, "***line %d: %s. \n", yyline, message);
}

int main (int argc, char **argv)
{
	int fd;
	int c;
	if (argc > 1)
	{
		close (0);
		open (argv [1], O_RDONLY);
	}
	sprintf (tempfilename, "tmp%d.tmp", getpid ());
	tempfile = fopen (tempfilename, "w+");
	yyparse ();
	fclose (tempfile);
	printf (".symbol\n");
	print_table (symbol_table);
	fflush (stdout);
	fd = open (tempfilename, O_RDONLY);
	while (c = read (fd, buf, 1024))
	{
		write (1, buf, c);
	}
	close (fd);
	unlink (tempfilename);
	return 0;
}

void print_data_string (FILE *stream, char *d)
{
	fprintf (stream, "%x ", (int) *d);
	if (*d)
	{
		print_data_string (stream, d + 1);
	}
}

